#include "graphics.opengl/texture.h"

namespace dw
{ 
	namespace graphics
	{
		namespace opengl
		{

			Texture::Texture() : _glId(0)
			{
				glGenTextures(1, &_glId);
			}

			Texture::Texture(const Type& type, const PixelFormat& pixelFormat, const unsigned width, const unsigned height, const Usage& usage) :
				ITexture(type, pixelFormat, width, height, usage), _glId(0)
			{
				glGenTextures(1, &_glId);
			}

			Texture::~Texture()
			{
				unload();
			}

			void Texture::load(std::istream& is)
			{
				//glGenTextures(1, &_glId);
				ITexture::load(is);
			}

			void Texture::unload()
			{
				glDeleteTextures(1, &_glId);
			}

			void Texture::resize(const uint32 size)
			{
				resize(0, size);
			}

			void Texture::resize(const unsigned mipmap, const uint32 size)
			{
				uint32 tmp = static_cast< uint32 >(std::pow((double)2, (double)mipmap));
				uint32 mipmapWidth		= _width	/ tmp;
				uint32 mipmapHeight	= _height	/ tmp;
				uint32 mipmapDepth		= _depth	/ tmp;

				if (mipmap == 0)
					_size = size;
				glBindTexture(glType(), _glId);
				glTexParameteri(glType(), GL_GENERATE_MIPMAP, GL_TRUE);
				//glTexParameteri(glType(), GL_TEXTURE_MAX_LEVEL, _numMipMaps);
				glTexParameteri(glType(), GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
				glTexParameteri(glType(), GL_TEXTURE_MAG_FILTER, GL_LINEAR);
				//glTexParameteri(glType(), GL_TEXTURE_MIN_FILTER, GL_NEAREST);
				//glTexParameteri(glType(), GL_TEXTURE_MAG_FILTER, GL_NEAREST);
				glTexParameteri(glType(), GL_TEXTURE_WRAP_S, GL_REPEAT);
				glTexParameteri(glType(), GL_TEXTURE_WRAP_T, GL_REPEAT);

				int numMipMaps = _numMipMaps;
				switch (_type)
				{
				case TEXTURE_1D	: 
					glTexImage1D(glType(), mipmap, glInternalFormat() ? glInternalFormat() : glFormat(), mipmapWidth, 0, glFormat(), GL_UNSIGNED_BYTE, 0); break ;
				case TEXTURE_2D	:
					glTexImage2D(glType(), mipmap, glInternalFormat() ? glInternalFormat() : glFormat(), mipmapWidth, mipmapHeight, 0, glFormat(), GL_UNSIGNED_BYTE, 0); break ;
				case TEXTURE_3D	: 
					glTexImage3D(glType(), mipmap, glInternalFormat() ? glInternalFormat() : glFormat(), mipmapWidth, mipmapHeight, mipmapDepth, 0, glFormat(), GL_UNSIGNED_BYTE, 0); break ;
				}
				if (GLenum glError = glGetError())
				{
					dwFail("qsd");
					throw std::runtime_error((const char*)gluErrorString(glError));
				}
			}

			void Texture::lock(const uint32 offset, const uint32 size, const Access access)
			{
				glBindTexture(glType(), _glId);
				_locked = true;
			}

			void Texture::unlock()
			{
				glBindTexture(glType(), _glId);
				_locked = false;
			}

			void Texture::read(const uint32 offset, const uint32 size, void* dst) const
			{
				glBindTexture(glType(), _glId);
				glGetTexImage(glType(), 0, glFormat(), GL_UNSIGNED_BYTE, dst);
			}

			void Texture::write(const uint32 offset, const uint32 size, const void* src)
			{
				write(0, offset, size, src);
			}

			void Texture::write(const unsigned mipmap, const uint32 offset, const uint32 size, const void* src)
            {
                uint32 tmp = static_cast< uint32 >(std::pow((double)2, (double)mipmap));
				uint32 mipmapWidth		= _width	/ tmp;
				uint32 mipmapHeight	= _height	/ tmp;
				uint32 mipmapDepth		= _depth	/ tmp;


				glBindTexture(glType(), _glId);
				switch (_type)
				{
				case TEXTURE_1D	:
					if (_compressed)
						glCompressedTexSubImage1D(GL_TEXTURE_1D, mipmap, offset,  mipmapWidth, glInternalFormat(), static_cast< uint32 >(_size / ::pow((double)4, (double)mipmap)), src);
					else
						glTexSubImage1D(GL_TEXTURE_1D, mipmap, offset,  mipmapWidth, glFormat(), GL_UNSIGNED_BYTE, src);
					break ;
				case TEXTURE_2D	:
					if (_compressed)
						glCompressedTexSubImage2D(GL_TEXTURE_2D, mipmap, offset, 0,  mipmapWidth, mipmapHeight, glInternalFormat(), static_cast< uint32 >(_size / ::pow((double)4, (double)mipmap)), src);
					else
						glTexSubImage2D(GL_TEXTURE_2D, mipmap, offset, 0,  mipmapWidth, mipmapHeight, glFormat(), GL_UNSIGNED_BYTE, src);
					break ;
				case TEXTURE_3D	: 
					if (_compressed)
						glCompressedTexSubImage3D(GL_TEXTURE_3D, mipmap, offset, 0, 0,  mipmapWidth, mipmapHeight, mipmapDepth, glInternalFormat(), static_cast< uint32 >(_size / ::pow((double)4, (double)mipmap)), src); 
					else
						glTexSubImage3D(GL_TEXTURE_3D, mipmap, offset, 0, 0,  mipmapWidth, mipmapHeight, mipmapDepth, glFormat(), GL_UNSIGNED_BYTE, src); 
					break ;
				}
				if (GLenum glError = glGetError())
					throw std::runtime_error((const char*)gluErrorString(glError));
			}

			void Texture::dispose()
			{
				delete this;
			}

			uint32 Texture::glType() const
			{
				switch (_type)
				{
				case TEXTURE_1D				: return GL_TEXTURE_1D;
				case TEXTURE_2D				: return GL_TEXTURE_2D;
				case TEXTURE_3D				: return GL_TEXTURE_3D;
				case TEXTURE_CUBE_MAP : return GL_TEXTURE_CUBE_MAP_ARB;
				default								:	return GL_ZERO;
				}
			}

			uint32 Texture::glInternalFormat() const
			{
				switch (_internalFormat)
				{
				case PF_INDEX			: return GL_RGBA;
        case PF_LUMINANCE	:	return GL_LUMINANCE;
        case PF_RGB				:	return GL_RGB;
        case PF_RGBA			:	return GL_RGBA;
				case PF_BGR				:	return GL_RGB;
        case PF_BGRA			:	return GL_RGBA;
        case PF_DXT1			:	return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
        case PF_DXT3			:	return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
				case PF_DXT5			:	return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
        default						:	return GL_ZERO;
        }
			}

			uint32 Texture::glFormat() const
			{
				switch (_pixelFormat)
				{
				case PF_INDEX			: return GL_COLOR_INDEX;
        case PF_LUMINANCE	:	return GL_LUMINANCE;
        case PF_RGB				:	return GL_RGB;
        case PF_RGBA			:	return GL_RGBA;
				case PF_BGR				:	return GL_BGR_EXT;
        case PF_BGRA			:	return GL_BGRA_EXT;
        case PF_DXT1			:	return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
        case PF_DXT3			:	return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
				case PF_DXT5			:	return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
        default						:	return GL_ZERO;
        }
			}

		} // namespace opengl
	} // namespace graphics 
} // namespace dw
